// import com.alibaba.druid.DbType;
// import com.alibaba.druid.sql.ast.SQLExpr;
// import com.alibaba.druid.sql.ast.SQLName;
// import com.alibaba.druid.sql.ast.expr.*;
// import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
// import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
// import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
// import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerOutput;
// import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerTop;
// import com.alibaba.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr;
// import com.alibaba.druid.sql.parser.Lexer;
// import com.alibaba.druid.sql.parser.SQLExprParser;
// import com.alibaba.druid.sql.parser.SQLParserFeature;
// import com.alibaba.druid.sql.parser.Token;
// import com.alibaba.druid.util.FnvHash;

// import java.util.Arrays;
// import java.util.List;

#include "SQLServerExprParser.h"
#include "../../../../DbType.h"
#include "../../../ast/SQLExpr.h"
#include "../../../ast/SQLName.h"
#include "../../../ast/expr/SQLSequenceExpr.h"
#include "../../../ast/statement/SQLColumnDefinition.h"
#include "../../../ast/statement/SQLExprTableSource.h"
#include "../../../ast/statement/SQLSelectItem.h"
#include "../ast/SQLServerOutput.h"
#include "../ast/SQLServerTop.h"
#include "../ast/expr/SQLServerObjectReferenceExpr.h"
#include "../../../parser/Lexer.h"
#include "../../../parser/SQLParserFeature.h"
#include "../../../parser/Token.h"
#include "../../../../utils/FnvHash.h"
#include "../../../../utils/instanceof.h"
#include "SQLServerLexer.h"
#include "SQLServerSelectParser.h"

std::shared_ptr<std::vector<string_ptr>> SQLServerExprParser::AGGREGATE_FUNCTIONS = std::make_shared<std::vector<string_ptr>>();
std::shared_ptr<std::vector<long>> SQLServerExprParser::AGGREGATE_FUNCTIONS_CODES = std::make_shared<std::vector<long>>();

BOOL_ptr SQLServerExprParser::__init = SQLServerExprParser::init();
BOOL_ptr SQLServerExprParser::init()
{
  std::shared_ptr<std::vector<string_ptr>> strings = std::make_shared<std::vector<string_ptr>>(std::vector<string_ptr>{
      make_string_ptr("AVG"),
      make_string_ptr("COUNT"),
      make_string_ptr("FIRST_VALUE"),
      make_string_ptr("MAX"),
      make_string_ptr("MIN"),
      make_string_ptr("ROW_NUMBER"),
      make_string_ptr("STDDEV"),
      make_string_ptr("SUM")});
  AGGREGATE_FUNCTIONS_CODES = FnvHash::fnv1a_64_lower(strings, true);
  // AGGREGATE_FUNCTIONS = new string_ptr[AGGREGATE_FUNCTIONS_CODES.length];
  for (size_t i = 0; i < AGGREGATE_FUNCTIONS_CODES->size(); i++)
  {
    AGGREGATE_FUNCTIONS->push_back(make_string_ptr(""));
  }
  for (string_ptr str : *strings)
  {
    long hash = FnvHash::fnv1a_64_lower(str);
    // int index = Arrays.binarySearch(AGGREGATE_FUNCTIONS_CODES, hash);
    int index = -1;
    int size = strings->size();
    for (int i = 0; i < size; i++)
    {
      // LOG_INFO << __FUNCTION__ << ": i:" << i << ",size:" << size;
      if (SQLServerExprParser::AGGREGATE_FUNCTIONS_CODES->at(i) == hash)
      {
        index = i;
        break;
      }
    }
    AGGREGATE_FUNCTIONS->at(index) = str;
  }
}

SQLServerExprParser::SQLServerExprParser(Lexer_ptr lexer)
    : SQLExprParser(lexer)
{
  this->dbType = DbType_ptr(new DbType(&DbType::sqlserver));
  this->aggregateFunctions = AGGREGATE_FUNCTIONS;
  this->aggregateFunctionHashCodes = AGGREGATE_FUNCTIONS_CODES;
}

SQLServerExprParser::SQLServerExprParser(string_ptr sql)
    : SQLServerExprParser(SQLServerLexer_ptr(new SQLServerLexer(sql)))
{

  this->lexer->nextToken();
  this->dbType = DbType_ptr(new DbType(&DbType::sqlserver));
}

SQLServerExprParser::SQLServerExprParser(string_ptr sql, SQLParserFeature_list_ptr features)
    : SQLServerExprParser(SQLServerLexer_ptr(new SQLServerLexer(sql, features)))
{

  this->lexer->nextToken();
  this->dbType = DbType_ptr(new DbType(&DbType::sqlserver));
}

SQLExpr_ptr SQLServerExprParser::primary()
{
  if (lexer->token()->name->c_str() == Token::LBRACKET.name->c_str())
  {
    lexer->nextToken();
    SQLExpr_ptr name = this->name();
    accept(Token::RBRACKET);
    return primaryRest(name);
  }

  return SQLExprParser::primary();
}

SQLSelectParser_ptr SQLServerExprParser::createSelectParser()
{
  return SQLServerSelectParser_ptr(new SQLServerSelectParser(SQLServerExprParser_ptr(this)));
}

SQLExpr_ptr SQLServerExprParser::primaryRest(SQLExpr_ptr expr)
{
  Token_ptr token = lexer->token();
  if (token->name->c_str() == Token::DOTDOT.name->c_str())
  {
    expr = nameRest(std::dynamic_pointer_cast<SQLName>(expr));
  }
  else if (lexer->identifierEquals(FnvHash::Constants::VALUE) && instanceof <SQLExpr, SQLIdentifierExpr>(expr))
  {
    SQLIdentifierExpr_ptr identExpr = std::dynamic_pointer_cast<SQLIdentifierExpr>(expr);
    if (identExpr->nameHashCode64() == FnvHash::Constants::NEXT)
    {
      lexer->nextToken();
      accept(Token::FOR);

      SQLName_ptr name = this->name();
      SQLSequenceExpr_ptr seq = SQLSequenceExpr_ptr(new SQLSequenceExpr());
      seq->setSequence(name);
      seq->setFunction(SQLSequenceExpr_Function_ptr(new SQLSequenceExpr_Function(&SQLSequenceExpr_Function::NextVal)));
      expr = seq;
    }
  }

  return SQLExprParser::primaryRest(expr);
}

SQLExpr_ptr SQLServerExprParser::dotRest(SQLExpr_ptr expr)
{
  BOOL_ptr backet = BOOL::FALSE;

  if (lexer->token()->name->c_str() == Token::LBRACKET.name->c_str())
  {
    lexer->nextToken();
    backet = BOOL::TRUE;
  }

  expr = SQLExprParser::dotRest(expr);

  if (backet)
  {
    accept(Token::RBRACKET);
  }

  return expr;
}

SQLName_ptr SQLServerExprParser::nameRest(SQLName_ptr expr)
{
  if (lexer->token()->name->c_str() == Token::DOTDOT.name->c_str())
  {
    lexer->nextToken();

    BOOL_ptr backet = BOOL::FALSE;
    if (lexer->token()->name->c_str() == Token::LBRACKET.name->c_str())
    {
      lexer->nextToken();
      backet = BOOL::TRUE;
    }
    string_ptr text = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    lexer->nextToken();

    if (backet)
    {
      accept(Token::RBRACKET);
    }

    SQLServerObjectReferenceExpr_ptr owner = SQLServerObjectReferenceExpr_ptr(new SQLServerObjectReferenceExpr(expr));
    expr = SQLPropertyExpr_ptr(new SQLPropertyExpr(owner, text));
  }

  return SQLExprParser::nameRest(expr);
}

SQLServerTop_ptr SQLServerExprParser::parseTop()
{
  if (lexer->token()->name->c_str() == Token::TOP.name->c_str())
  {
    SQLServerTop_ptr top = SQLServerTop_ptr(new SQLServerTop());
    lexer->nextToken();

    BOOL_ptr paren = BOOL::FALSE;
    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      paren = BOOL::TRUE;
      lexer->nextToken();
    }

    if (lexer->token()->name->c_str() == Token::LITERAL_INT.name->c_str())
    {
      top->setExpr(lexer->integerValue());
      lexer->nextToken();
    }
    else
    {
      top->setExpr(primary());
    }

    if (paren)
    {
      accept(Token::RPAREN);
    }

    if (lexer->token()->name->c_str() == Token::PERCENT.name->c_str())
    {
      lexer->nextToken();
      top->setPercent(BOOL::TRUE);
    }

    return top;
  }

  return nullptr;
}

SQLServerOutput_ptr SQLServerExprParser::parserOutput()
{
  if (lexer->identifierEquals(make_string_ptr("OUTPUT")))
  {
    lexer->nextToken();
    SQLServerOutput_ptr output = SQLServerOutput_ptr(new SQLServerOutput());

    SQLSelectItem_list_ptr selectList = output->getSelectList();
    for (;;)
    {
      SQLSelectItem_ptr selectItem = parseSelectItem();
      selectList->push_back(selectItem);

      if (lexer->token()->name->c_str() != Token::COMMA.name->c_str())
      {
        break;
      }

      lexer->nextToken();
    }

    if (lexer->token()->name->c_str() == Token::INTO.name->c_str())
    {
      lexer->nextToken();
      output->setInto(SQLExprTableSource_ptr(new SQLExprTableSource(this->name())));
      if (lexer->token()->name->c_str() == (Token::LPAREN.name->c_str()))
      {
        lexer->nextToken();
        this->exprList(output->getColumns(), output);
        accept(Token::RPAREN);
      }
    }
    return output;
  }
  return nullptr;
}

SQLSelectItem_ptr SQLServerExprParser::parseSelectItem()
{
  SQLExpr_ptr expr;
  if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
  {
    expr = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()))));
    lexer->nextTokenComma();

    if (lexer->token()->name->c_str() != Token::COMMA.name->c_str())
    {
      expr = this->primaryRest(expr);
      expr = this->exprRest(expr);
    }
  }
  else
  {
    expr = this->expr();
  }
  string_ptr alias = as();
  return SQLSelectItem_ptr(new SQLSelectItem(expr, alias));
}

SQLColumnDefinition_ptr SQLServerExprParser::createColumnDefinition()
{
  SQLColumnDefinition_ptr column = SQLColumnDefinition_ptr(new SQLColumnDefinition());
  column->setDbType(dbType);
  return column;
}

SQLColumnDefinition_ptr SQLServerExprParser::parseColumnRest(SQLColumnDefinition_ptr column)
{
  if (lexer->token()->name->c_str() == Token::IDENTITY.name->c_str())
  {
    lexer->nextToken();

    Identity_ptr identity = Identity_ptr(new Identity());
    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();

      SQLIntegerExpr_ptr seed = std::dynamic_pointer_cast<SQLIntegerExpr>(this->primary());
      accept(Token::COMMA);
      SQLIntegerExpr_ptr increment = std::dynamic_pointer_cast<SQLIntegerExpr>(this->primary());
      accept(Token::RPAREN);

      identity->setSeed((int)seed->getNumber());
      identity->setIncrement((int)increment->getNumber());
    }

    if (lexer->token()->name->c_str() == Token::NOT.name->c_str())
    {
      lexer->nextToken();

      if (lexer->token()->name->c_str() == Token::XNULL.name->c_str())
      {
        lexer->nextToken();
        column->setDefaultExpr(SQLNullExpr_ptr(new SQLNullExpr()));
      }
      else
      {
        accept(Token::FOR);
        acceptIdentifier("REPLICATION ");
        identity->setNotForReplication(BOOL::TRUE);
      }
    }

    column->setIdentity(identity);
  }

  return SQLExprParser::parseColumnRest(column);
}
